#include "oh/RenderNodeImplC.h"
#include "src/core/SkTraceEvent.h"

#include <arkui/native_node.h>
#include <arkui/native_interface.h>

inline ArkUI_NativeNodeAPI_1 *GetNativeNodeAPI()
{
    static ArkUI_NativeNodeAPI_1 *api = nullptr;
    if (!api) {
        OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, api);
    }
    return api;
}

void RenderNodeImplC::initNode(void *UINode, OHRenderNode *selfNode) {
    if (UINode) {
        fExternalNode = (ArkUI_NodeHandle)UINode;
        // NOTE: make sure ArkUI_NodeHandle has been adopted
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_GetRenderNode(fExternalNode, &fExternalRenderNode);
//        return;
    }
    fNodeHandle = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_CreateNode();
    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetPivot(fNodeHandle, 0.0f, 0.0f);
    if (fNodeHandle == nullptr) {
        SkDebugf("OHRenderNodeC create failed!");
    }
    auto onDrawCallBack = [](ArkUI_DrawContext* context, void* userData) {
        auto *canvas1 = OH_ArkUI_DrawContext_GetCanvas(context);
        OH_Drawing_Canvas *canvas = reinterpret_cast<OH_Drawing_Canvas *>(canvas1);
        if (userData) {
            OHRenderNode* node = (OHRenderNode*)userData;
            node->nodeDraw(canvas);
        }
    };
    fModiferHandle = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_CreateContentModifier();
    // @NOTE: Attach modifier must done before attach property
    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_AttachContentModifier(fNodeHandle, fModiferHandle);
    fInvalidateHandle = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_CreateColorProperty(0);
    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_AttachColorProperty(fModiferHandle, fInvalidateHandle);
    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetContentModifierOnDraw(fModiferHandle, selfNode, onDrawCallBack);
    // The default value of clipToFrame is different from ArkTs RenderNode
    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetClipToFrame(fNodeHandle, 1);
    if (UINode) {
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetClipToFrame(fNodeHandle, 0);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetSize(fNodeHandle, 1, 1);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_AddChild(fNodeHandle, fExternalRenderNode);
    }
}

RenderNodeImplC::~RenderNodeImplC() {
//    if (fExternalNode) {
//        return;
//    }
    if (fNodeHandle) {
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_DisposeNode(fNodeHandle);
    }
    if (fModiferHandle) {
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_DisposeContentModifier(fModiferHandle);
    }
    if (fInvalidateHandle) {
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_DisposeColorProperty(fInvalidateHandle);
    }
    // root RenderNode hold the Custom Node
    if (fCustomNode) {
        ArkUI_NativeNodeAPI_1* arkUINativeNodeApi = nullptr;
        OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, arkUINativeNodeApi);
        arkUINativeNodeApi->disposeNode(fCustomNode);
    }
}

void *RenderNodeImplC::getNodeObject() {
    return fNodeHandle;
}

void RenderNodeImplC::doRedraw(OHRenderNode *node) {
    NodeStatusModify::GetInstance()->setModifyNodeType(RenderNodeType::NODE_C);
    {
        // Used to trigger RequestNextVSync and must not be removed.
        ArkUI_NumberValue value[] = {{.i32 = 0}};
        ArkUI_AttributeItem item = {value, 1};
        GetNativeNodeAPI()->setAttribute(fCustomNode, NODE_RENDER_GROUP, &item);
    }
    node->doRedraw();
}

SkRect RenderNodeImplC::getNodeSize() {
    int32_t width;
    int32_t height;
    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_GetSize(fNodeHandle, &width, &height);
    return SkRect::MakeWH(width, height);
}

void RenderNodeImplC::appendRootNode(std::shared_ptr<OHRenderNode> root) {
    if (!root) {
        return;
    }
    if (root->getNodeType() == RenderNodeType::NODE_UNINIT) {
        root->setNodeImpl(std::make_unique<RenderNodeImplC>());
        root->initNode(nullptr);
    }
    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_AddChild(fNodeHandle, (ArkUI_RenderNodeHandle)root->getNodeObject());
}

void RenderNodeImplC::nodeDraw(OH_Drawing_Canvas *oh_canvas, OHRenderNode *selfNode) {
    if (selfNode->fPictureCmd) {
        OH_Drawing_CanvasSave(oh_canvas);
        int32_t width;
        int32_t height;
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_GetSize(fNodeHandle, &width, &height);
        SkRect rect = SkRect::MakeWH(width, height);
        OH_Drawing_CanvasClipRect(oh_canvas, (OH_Drawing_Rect *)&rect, OH_Drawing_CanvasClipOp::INTERSECT, false);
        OH_Drawing_CanvasTranslate(oh_canvas, selfNode->fOffsetX, selfNode->fOffsetY);
        OHDrawingAPI::OH_Drawing_CanvasDrawRecordCmdNesting(oh_canvas, selfNode->fPictureCmd.get());
        OH_Drawing_CanvasRestore(oh_canvas);
        selfNode->fNodeNeedRedraw = false;
    }
}

void RenderNodeImplC::doInvalidate() {
    uint32_t v;
    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_GetColorPropertyValue(fInvalidateHandle, &v);
    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetColorPropertyValue(fInvalidateHandle, v + 1);
}

void SetRenderNodeClip(ArkUI_RenderNodeHandle node, const NodeStatusModify::NodeModifyInfo &info) {
    ArkUI_RenderNodeClipOption* clip = nullptr;
    if (info.fOp == NodeStatusModify::NodeOp::SHAPE_CLIP) {
        auto rectClip = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_CreateRectShapeOption();
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRectShapeOptionEdgeValue(rectClip, info.fNumbers[0], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_LEFT_TEMP);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRectShapeOptionEdgeValue(rectClip, info.fNumbers[1], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_TOP_TEMP);
        if (OHRenderNode::gCAPIFixed) {
            OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRectShapeOptionEdgeValue(rectClip, info.fNumbers[2], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_RIGHT_TEMP);
            OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRectShapeOptionEdgeValue(rectClip, info.fNumbers[3], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_BOTTOM_TEMP);
        } else {
            OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRectShapeOptionEdgeValue(rectClip, info.fNumbers[2] - info.fNumbers[0], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_RIGHT_TEMP);
            OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRectShapeOptionEdgeValue(rectClip, info.fNumbers[3] - info.fNumbers[1], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_BOTTOM_TEMP);
        }
        clip = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_CreateRenderNodeClipOptionFromRectShape(rectClip);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_DisposeRectShapeOption(rectClip);
    } else if (info.fOp == NodeStatusModify::NodeOp::SHAPE_CLIP_RRECT) {
        auto rrectClip = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_CreateRoundRectShapeOption();
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRoundRectShapeOptionEdgeValue(rrectClip, info.fNumbers[0], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_LEFT_TEMP);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRoundRectShapeOptionEdgeValue(rrectClip, info.fNumbers[1], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_TOP_TEMP);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRoundRectShapeOptionEdgeValue(rrectClip, info.fNumbers[2], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_RIGHT_TEMP);	
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRoundRectShapeOptionEdgeValue(rrectClip, info.fNumbers[3], ArkUI_EdgeDirection_Temp::ARKUI_EDGE_DIRECTION_BOTTOM_TEMP);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRoundRectShapeOptionCornerXY(rrectClip, info.fNumbers[4], info.fNumbers[5], ArkUI_CornerDirection_Temp::ARKUI_CORNER_DIRECTION_TOP_LEFT_TEMP);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRoundRectShapeOptionCornerXY(rrectClip, info.fNumbers[6], info.fNumbers[7], ArkUI_CornerDirection_Temp::ARKUI_CORNER_DIRECTION_TOP_RIGHT_TEMP);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRoundRectShapeOptionCornerXY(rrectClip, info.fNumbers[8], info.fNumbers[9], ArkUI_CornerDirection_Temp::ARKUI_CORNER_DIRECTION_BOTTOM_RIGHT_TEMP);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRoundRectShapeOptionCornerXY(rrectClip, info.fNumbers[10], info.fNumbers[11], ArkUI_CornerDirection_Temp::ARKUI_CORNER_DIRECTION_BOTTOM_LEFT_TEMP);
        clip = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_CreateRenderNodeClipOptionFromRoundRectShape(rrectClip);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_DisposeRoundRectShapeOption(rrectClip);
    } else if (info.fOp == NodeStatusModify::NodeOp::SHAPE_CLIP_PATH) {
        auto pathClip = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_CreateCommandPathOption();
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetCommandPathOptionCommands(pathClip, const_cast<char*>(info.fStr.c_str()));
        clip = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_CreateRenderNodeClipOptionFromCommandPath(pathClip);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_DisposeCommandPathOption(pathClip);
    } else {
        assert(0);
    }
    if (clip != nullptr) {
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetClip(node, clip);
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_DisposeRenderNodeClipOption(clip);
    }
}

// Deprecate this function due to differences between the clip attribute and RenderNode
void SetFrameNodeClip(ArkUI_NodeHandle node, const NodeStatusModify::NodeModifyInfo &info) {
    ArkUI_NumberValue values[12] = {};
    ArkUI_AttributeItem item = {};

    if (info.fOp == NodeStatusModify::NodeOp::SHAPE_CLIP) {
        values[0].i32 = static_cast<int32_t>(ARKUI_CLIP_TYPE_RECTANGLE);
        values[1].f32 = static_cast<float>((info.fNumbers[2] - info.fNumbers[0]) / OHRenderNode::gVP2PXRatio);
        values[2].f32 = static_cast<float>((info.fNumbers[3] - info.fNumbers[1]) / OHRenderNode::gVP2PXRatio);
        item = {values, 3, nullptr};
        GetNativeNodeAPI()->setAttribute(node, NODE_CLIP_SHAPE, &item);
    } else if (info.fOp == NodeStatusModify::NodeOp::SHAPE_CLIP_RRECT) {
        values[0].i32 = static_cast<int32_t>(ARKUI_CLIP_TYPE_RECTANGLE);
        values[1].f32 = static_cast<float>((info.fNumbers[2] - info.fNumbers[0]) / OHRenderNode::gVP2PXRatio);
        values[2].f32 = static_cast<float>((info.fNumbers[3] - info.fNumbers[1]) / OHRenderNode::gVP2PXRatio);
        values[5].f32 = static_cast<float>(info.fNumbers[4]);  // top-left
        values[6].f32 = static_cast<float>(info.fNumbers[10]); // bottom-left
        values[7].f32 = static_cast<float>(info.fNumbers[6]);  // top-right
        values[8].f32 = static_cast<float>(info.fNumbers[8]);  // bottom-right
        item = {values, 9, nullptr};
        GetNativeNodeAPI()->setAttribute(node, NODE_CLIP_SHAPE, &item);
    } else if (info.fOp == NodeStatusModify::NodeOp::SHAPE_CLIP_PATH) {
        values[0].i32 = static_cast<int32_t>(ARKUI_CLIP_TYPE_PATH);
        values[1].f32 = 0.0f;
        values[2].f32 = 0.0f;
        item = {values, 3, const_cast<char *>(info.fStr.c_str())};
        GetNativeNodeAPI()->setAttribute(node, NODE_CLIP_SHAPE, &item);
    } else {
        assert(0);
    }
}

void RenderNodeImplC::FillOpsForBatch(NodeStatusModify::NodeBatchContext &ctx,
                                      const NodeStatusModify::NodeModifyInfo &info, void *const (&op_nodes)[3]) {
    using Op = NodeStatusModify::NodeOp;
    ArkUI_RenderNodeHandle nodes[3];
    ArkUI_NodeHandle extNode = nullptr;
    std::shared_ptr<OHRenderNode> node = nullptr;
    for (size_t i = 0; i < 3; i++) {
        nodes[i] = (ArkUI_RenderNodeHandle)op_nodes[i];
    }
    if (node = info.fNodes[0].lock()) {
        extNode = node->getExternalNode();
    }
    switch (info.fOp) {
    case Op::INVALIDATE: {
        if (auto node = info.fNodes[0].lock()) {
            node->doInvalidate();
        }
        break;
    }
    case Op::REMOVE_CHILD:
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_AddChild(nodes[0], nodes[1]);
        break;
    case Op::INSERT_CHILD_AFTER:
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_InsertChildAfter(nodes[0], nodes[1], nodes[2]);
        break;
    case Op::CLEAR_CHILDREN:
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_ClearChildren(nodes[0]);
        break;
    case Op::SIZE:
        if (extNode) {
            ArkUI_NumberValue size[] = {{.f32 = (float)(info.fNumbers[0] / OHRenderNode::gVP2PXRatio)},
                                        {.f32 = (float)(info.fNumbers[1] / OHRenderNode::gVP2PXRatio)}};
            ArkUI_AttributeItem itemSize = {size, 2};
            GetNativeNodeAPI()->setAttribute(extNode, NODE_SIZE, &itemSize);
        } else {
            OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetSize(nodes[0], (int32_t)(info.fNumbers[0]),
                                                           (int32_t)(info.fNumbers[1]));
        }
        break;
    case Op::POSITION:
        if (extNode) {
            ArkUI_NumberValue pos[] = {{.f32 = (float)(info.fNumbers[0] / OHRenderNode::gVP2PXRatio)},
                                        {.f32 = (float)(info.fNumbers[1] / OHRenderNode::gVP2PXRatio)}};
            ArkUI_AttributeItem itemPos = {pos, 2};
            GetNativeNodeAPI()->setAttribute(extNode, NODE_POSITION, &itemPos);
        } else {
            OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetPosition(nodes[0], (int32_t)(info.fNumbers[0]),
                                                               (int32_t)(info.fNumbers[1]));
        }
        break;
    case Op::TRANSFORM: {
        if (extNode) {
            ArkUI_NumberValue numValues[16];
            for (int i = 0; i < 16; i++) {
                numValues[i].f32 = info.fNumbers[i];
            }
            ArkUI_AttributeItem item = {numValues, 16};
            GetNativeNodeAPI()->setAttribute(extNode, NODE_TRANSFORM, &item);
        } else {
            if (OHRenderNode::gCAPIFixed) {
                OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetTransform(nodes[0], const_cast<float *>(info.fNumbers));
            } else {
                float a = info.fNumbers[SkMatrix::kMScaleX];
                float b = info.fNumbers[SkMatrix::kMSkewY];
                float c = info.fNumbers[SkMatrix::kMSkewX];
                float d = info.fNumbers[SkMatrix::kMScaleY];
                float tx = info.fNumbers[SkMatrix::kMTransX];
                float ty = info.fNumbers[SkMatrix::kMTransY];
                float rotationZ = std::atan2(b, a) * 180.0f / M_PI;
                float scaleX = std::sqrt(a * a + b * b);
                float scaleY = std::sqrt(c * c + d * d);

                OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetTranslation(nodes[0], tx, ty);
                OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetScale(nodes[0], scaleX, scaleY);
                OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetRotation(nodes[0], 0.0f, 0.0f, rotationZ);
            }
        }
        break;
    }
    case Op::SHAPE_CLIP:
    case Op::SHAPE_CLIP_RRECT:
    case Op::SHAPE_CLIP_PATH:
        if (extNode) {
            // at this time, nodes[0] is the proxy rendernode
            SetRenderNodeClip(nodes[0], info);
        } else {
            SetRenderNodeClip(nodes[0], info);
        }
        break;
    case Op::APPEND_CHILD:
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_AddChild(nodes[0], nodes[1]);
        break;
    case Op::MARK_GROUP:
        if (extNode) {
            ArkUI_NumberValue value[] = {{.i32 = 1}};
            ArkUI_AttributeItem item = {value, 1};
            GetNativeNodeAPI()->setAttribute(extNode, NODE_RENDER_GROUP, &item);
        } else {
            OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetMarkNodeGroup(nodes[0], true);
        }
        break;
    case Op::MARK_NO_GROUP: {
        if (extNode) {
            ArkUI_NumberValue value[] = {{.i32 = 0}};
            ArkUI_AttributeItem item = {value, 1};
            GetNativeNodeAPI()->setAttribute(extNode, NODE_RENDER_GROUP, &item);
        } else {
            OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetMarkNodeGroup(nodes[0], false);
        }
        break;
    }
    default:
        break;
    }
}

void RenderNodeImplC::ApplyBatchModify(NodeStatusModify::NodeBatchContext &ctx, napi_value localObject) {
}

bool RootRenderNodeManager::RegisterNode(int32_t id, std::unique_ptr<OHRenderNode> node) {
    std::lock_guard<std::mutex> lock(mutex_);
    ArkUI_NodeHandle customNode = node->getCustomNode();
    auto [it, inserted] = rootNodes_.emplace(id, std::move(node));
    if (!inserted) {
        return false;
    }
    customNodeMap_[customNode] = it->second.get();
    handleToId_[customNode] = id;
    return true;
}

bool RootRenderNodeManager::UnregisterNode(int32_t id) {
    std::lock_guard<std::mutex> lock(mutex_);
    auto it = rootNodes_.find(id);
    if (it == rootNodes_.end()) {
        return false;
    }
    if (it->second->hasPendingAsyncTask()) {
        it->second->setPendingUnregisterNode();
        return true;
    }
    ArkUI_NodeHandle handle = it->second->getCustomNode();
    UnregisterNodeInternal(id, handle);
    return true;
}

bool RootRenderNodeManager::UnregisterNode(ArkUI_NodeHandle handle) {
    std::lock_guard<std::mutex> lock(mutex_);
    auto it = handleToId_.find(handle);
    if (it == handleToId_.end()) {
        return false;
    }
    int32_t id = it->second;
    auto nodeIt = rootNodes_.find(id);
    if (nodeIt->second->hasPendingAsyncTask()) {
        nodeIt->second->setPendingUnregisterNode();
        return true;
    }
    UnregisterNodeInternal(id, handle);
    return true;
}

void RootRenderNodeManager::NotifyRedraw(OHRenderNode *node) {
    if (node->fAsyncTask && !node->fHasPendingAsyncTask) {
        node->fHasPendingAsyncTask = true;
        uv_async_send(node->fAsyncTask);
    }
}

// same with RenderNodeNotifyRedraw
void RootRenderNodeManager::NotifyRedraw(int32_t id) {
    std::lock_guard<std::mutex> lock(mutex_);
    auto it = rootNodes_.find(id);
    if (it != rootNodes_.end()) {
        NotifyRedraw(it->second.get());
    }
}

void RootRenderNodeManager::Resize(OHRenderNode *node, int32_t width, int32_t height) {
    // Make sure that we set the root rendernode
    if (node->fCallbackC) {
        auto renderNode = (ArkUI_RenderNodeHandle)node->getNodeObject();
        auto customNode = node->getCustomNode();
        OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetSize(renderNode, width, height);
        ArkUI_NumberValue size[] = {{.f32 = (float)(width / OHRenderNode::gVP2PXRatio)},
                                    {.f32 = (float)(height / OHRenderNode::gVP2PXRatio)}};
        ArkUI_AttributeItem itemSize = {size, 2};
        GetNativeNodeAPI()->setAttribute(customNode, NODE_SIZE, &itemSize);
    }
}

void RootRenderNodeManager::Resize(int32_t id, int32_t width, int32_t height) {
    std::lock_guard<std::mutex> lock(mutex_);
    auto it = rootNodes_.find(id);
    if (it != rootNodes_.end()) {
        Resize(it->second.get(), width, height);
    }
}

OHRenderNode *RootRenderNodeManager::FindNodeByCustomNode(ArkUI_NodeHandle handle) {
    std::lock_guard<std::mutex> lock(mutex_);
    auto it = customNodeMap_.find(handle);
    if (it != customNodeMap_.end()) {
        return it->second;
    }
    return nullptr;
}

bool RootRenderNodeManager::AdoptNode(int32_t id, ArkUI_NodeHandle handle) {
    std::lock_guard<std::mutex> lock(mutex_);
    auto it = rootNodes_.find(id);
    if (it == rootNodes_.end()) {
        return false;
    }
    ArkUI_NodeHandle parent = it->second->getCustomNode();
    OHDrawingAPI::OH_ArkUI_NativeModule_AdoptChild(parent, handle);
    adoptedChildren_[id].push_back(handle);
    return true;
}

void RootRenderNodeManager::MapAdoptNode(int32_t id, int32_t extNodeId, std::weak_ptr<OHRenderNode> node) {
    std::lock_guard<std::mutex> lock(mutex_);
    int64_t key = (static_cast<uint64_t>(static_cast<uint32_t>(id)) << 32) | static_cast<uint32_t>(extNodeId);
    adoptedNodeMap_[key] = node;
}

std::shared_ptr<OHRenderNode> RootRenderNodeManager::GetAdoptOHRenderNode(int32_t id, int32_t extNodeId) {
    std::lock_guard<std::mutex> lock(mutex_);
    int64_t key = (static_cast<uint64_t>(static_cast<uint32_t>(id)) << 32) | static_cast<uint32_t>(extNodeId);
    auto it = adoptedNodeMap_.find(key);
    if (it == adoptedNodeMap_.end()) {
        return nullptr;
    }
    if (auto node = it->second.lock()) {
        return node;
    }
    return nullptr;
}

void RootRenderNodeManager::UnregisterNodeInternal(int32_t id, ArkUI_NodeHandle handle) {
    auto adoptIt = adoptedChildren_.find(id);
    if (adoptIt != adoptedChildren_.end()) {
        for (auto child : adoptIt->second) {
            OHDrawingAPI::OH_ArkUI_NativeModule_RemoveAdoptedChild(handle, child);
            // dispose the proxy FrameNode for external FrameNode
            GetNativeNodeAPI()->disposeNode(child);
        }
        adoptedChildren_.erase(adoptIt);
    }
    customNodeMap_.erase(handle);
    handleToId_.erase(handle);
    rootNodes_.erase(id);
}

std::pair<ArkUI_NodeHandle, std::unique_ptr<OHRenderNode>>
OHRenderNode::InitRootRenderNode(NodeDrawCallbackC callback) {
    auto customNode = GetNativeNodeAPI()->createNode(ARKUI_NODE_CUSTOM);
    auto rootNode = std::make_unique<OHRenderNode>();
    rootNode->setNodeImpl(std::make_unique<RenderNodeImplC>());
    rootNode->initNode(nullptr);
    rootNode->fCallbackC = callback;
    (void)rootNode->setLiveWithJs();

    auto renderRootNode = (ArkUI_RenderNodeHandle)rootNode->getNodeObject();
    auto result = OHDrawingAPI::OH_ArkUI_RenderNodeUtils_AddRenderNode(customNode, renderRootNode);
    if (result != ARKUI_ERROR_CODE_NO_ERROR) {
        return {nullptr, nullptr};
    }

    OHDrawingAPI::OH_ArkUI_RenderNodeUtils_SetPosition(renderRootNode, 0, 0);

    ArkUI_NumberValue value[] = {{.f32 = 1.0f}};
    ArkUI_AttributeItem item = {value, 1};
    GetNativeNodeAPI()->setAttribute(customNode, NODE_WIDTH_PERCENT, &item);
    GetNativeNodeAPI()->setAttribute(customNode, NODE_HEIGHT_PERCENT, &item);

    rootNode->setCustomNode(customNode);
    return {customNode, std::move(rootNode)};
}

ArkUI_NodeHandle OHRenderNode::CreateRootRenderNodeWithoutEvent(int32_t id, NodeDrawCallbackC callback) {
    auto [customNode, rootNode] = InitRootRenderNode(callback);
    if (!customNode || !rootNode) {
        SkDebugf("CAPI root rendernode add failed, id: %d", id);
        return nullptr;
    }
    SkDebugf("CAPI root rendernode add success, id: %d", id);
    rootNode->fRootNodeId = id;
    RootRenderNodeManager::Instance().RegisterNode(id, std::move(rootNode));
    return customNode;
}

std::unique_ptr<OHRenderNode> OHRenderNode::CreateRootRenderNodeWithoutEventV2(NodeDrawCallbackC callback) {
    auto [customNode, rootNode] = InitRootRenderNode(callback);
    return (!customNode || !rootNode) ? nullptr : std::move(rootNode);
}
